#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include "transContentDispatcher.hpp"
#include "transContentPortMgr.hpp"

TransContentDispatcher* TransContentDispatcher::_instance = NULL;
int TransContentDispatcher::stackRunningFlag = 0;

TransContentDispatcher* TransContentDispatcher::getInstance(){
	debug_enter();
	if(_instance == NULL){
		logd_info("start to create dispathcer instance");
		_instance = new TransContentDispatcher();
		if(_instance->getState() != INS_STATE_OK){
			logd_error("create dispathcer instance failed!!");
			releaseInstance();
		}
	}
	debug_leave();
	return _instance;
}

void TransContentDispatcher::releaseInstance(){
	debug_enter();
	if(_instance != NULL){
		log_info("start to release the disptacher instance!");
		delete _instance;
		_instance = NULL;
	}
	debug_leave();
}

int TransContentDispatcher::getState(){
	return this->mState;
}

int TransContentDispatcher::sendDataDataToStack(const char* content, int length){
	debug_enter();
	if(mState != INS_STATE_OK){
		log_error("dispathcer istance state failed!!");
		return E_TRANC_InsStateFailed;
	}
	dumpPacket(content, length)
	pthread_mutex_lock(&mutex_dataOut);
	if(write(fd_fifo_dataOut_w, content, length) != length){
		logd_error("write data content to stack failed!!i: %s", strerror(errno));
	}
	pthread_mutex_unlock(&mutex_dataOut);
	return E_TRANC_SUCESS;
}

int TransContentDispatcher::sendDataRegistToStack(const char* content, int length){
	debug_enter();
	if(mState != INS_STATE_OK){
		log_error("dispathcer istance state failed!!");
		return E_TRANC_InsStateFailed;
	}
	dumpPacket(content, length);
	pthread_mutex_lock(&mutex_regist);
	if(write(fd_fifo_regist_w, content, length) != length){
		logd_error("write regist content to stack failed!!, error=%s", strerror(errno));
	}
	pthread_mutex_unlock(&mutex_regist);
	return E_TRANC_SUCESS;
}

int TransContentDispatcher::openFifoRegist(){	
	debug_enter();
	//check exist
	if(access(stackFIFORegistName, F_OK|W_OK)==-1){
		log_error("check regist fifo:%s exist or write able failed: %s", stackFIFORegistName, strerror(errno));
		return E_TRANC_SystemError;
	}
	fd_fifo_regist_w = open(stackFIFORegistName, O_WRONLY);
	if(fd_fifo_regist_w == -1){
		log_error("open regist fifo failed!:%s", strerror(errno));
		return E_TRANC_SystemError;
	}
	return E_TRANC_SUCESS;
}

int TransContentDispatcher::openFifoDataOut(){	
	//check exist
	debug_enter();
	if(access(stackFIFODataOutName, F_OK|W_OK)==-1){
		log_error("check data fifo:%s exist or write able failed: %s", stackFIFODataOutName, strerror(errno));
		return E_TRANC_SystemError;
	}

	//read out fd
	fd_fifo_dataOut_w = open(stackFIFODataOutName, O_WRONLY);
	if(fd_fifo_dataOut_w == -1){
		log_error("open data out fifo failed!:%s", strerror(errno));
		return E_TRANC_SystemError;
	}
	return E_TRANC_SUCESS;
}

int TransContentDispatcher::openFifoLocalFifo(){
	int tempfd;
	const char* localFifo = TransContentSetting::getLocalFifoName();
	mkdirp();
	//open no block
	tempfd = open(stackFIFODataOutName,O_RDONLY|O_NONBLOCK);
	if(tempfd == -1){
		log_error("open data fifo as read only failed!!; %s", strerror(errno));
		return E_TRANC_SystemError;
	}
	
	log_info("open local out fifo, as read only");
	fd_fifo_dataIn_r = open(stackFIFODataOutName,O_RDONLY);
	if(fd_fifo_dataIn_r == -1){
		close(tempfd);
		logd_error("open local out fifo failed!!, %s", strerror(errno));
		return E_TRANC_SystemError;
	}
	
	log_info("open local out fifo, as write only");
	fd_fifo_dataIn_w = open(stackFIFODataOutName,O_WRONLY);
	if(fd_fifo_dataIn_w == -1){
		close(tempfd);
		logd_error("open local out fifo failed!!, %s", strerror(errno));
		return E_TRANC_SystemError;
	}
	close(tempfd);
	return E_TRANC_SUCESS;
}

TransContentDispatcher::TransContentDispatcher(){
	debug_enter();
	mState = INS_STATE_ERROR;
	fd_fifo_dataOut_w = -1;
	fd_fifo_regist_w = -1;
	fd_fifo_dataIn_r = -1;
	fd_fifo_dataIn_w = -1;
	pthread_mutex_init(&mutex_dataIn, NULL);
	pthread_mutex_init(&mutex_dataOut, NULL);
	pthread_mutex_init(&mutex_regist, NULL);
	memInstance = TransContentMemory::getInstance();
	if(openFifoRegist() != E_TRANC_SUCESS){
		logd_error("dispatcher, open the reigst fifo error");
		return;
	}

	if(openFifoDataOut()!= E_TRANC_SUCESS){
		logd_error("dispatcher, open the data out fifo error");
		return;
	}

	if(openFifoLocalFifo()!= E_TRANC_SUCESS){
		logd_error("dispatcher, open the local fifo error");
		return;
	}
	mState = INS_STATE_OK;
	debug_leave();
}

TransContentDispatcher::~TransContentDispatcher(){
	debug_enter();
	mState = INS_STATE_ERROR;
	if(fd_fifo_dataOut_w != -1){
		close(fd_fifo_dataOut_w);
	}
	if(fd_fifo_regist_w != -1){
		close(fd_fifo_regist_w);
	}
	if(fd_fifo_dataIn_r != -1){
		close(fd_fifo_dataIn_r);
	}
	if(fd_fifo_dataIn_w != -1){
		close(fd_fifo_dataIn_w);
	}
	pthread_mutex_destroy(&mutex_dataIn);
	pthread_mutex_destroy(&mutex_dataOut);
	pthread_mutex_destroy(&mutex_regist);
	log_warm("distpatcher has been destoried!!");
}

//////////////////////////////////////////////////
/// check stack state
/// check the fifo exist, if not exist, set flag to 0
/// check the fifo openable, if open failed set falg to 0
///		the main thread create this thread to check, the main thread sleep 3 second, after sleep cancle the thread, no matter the thread exist or not
/// if the fifo exist and can open as write set the running state to 1
int TransContentDispatcher::checkStackIsReady(){
	int tempfd = 0;
	if(access(stackFIFORegistName, F_OK|W_OK)==-1){
		stackRunningFlag = 0;
		logd_error("dispathcer, theck the stack not create the regist fifo");
		return 0;
	}

	// open as write only will not block
	if((tempfd = open(stackFIFORegistName, O_WRONLY)) == -1 && errno == ENXIO){
		stackRunningFlag = 0;
		log_info("check stack state thread error, the stack not open as read!");
		return NULL;
	}
	close(tempfd);
	log_info("check stack state thread sucerss");
	stackRunningFlag = 1;
	return NULL;
}

int TransContentDispatcher::isStackRunning(){
	checkStackIsReady();
	return stackRunningFlag;
}

//////////////////////////////////////////////////////
/// create the directory of data file, after create chagne the privilege to 777
/// create the directory step by step inner
/////////////////////////////////////////////////////
void TransContentDispatcher::mkdirp(){
	const char* localDataDir = TransContentSetting::getLocalDataDir();
	const char* localDumpDir = TransContentSetting::getLocalDumpDir();

	debug_enter();

	mkdir(localDataDir, S_IRWXU|S_IRWXG|S_IRWXO);
	chmod(localDataDir, S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH);

	mkdir(localDumpDir, S_IRWXU|S_IRWXG|S_IRWXO);
	chmod(localDumpDir, S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH);
	debug_leave();
}

//////////////////////////////////////////////////
/// use fifo to get data from stack process
/// in this method, call portManager response, after response reutrn
/// each time the receive finished, the thread receive again
//////////////////////////////////////////////////
void TransContentDispatcher::receiveDataFromStack(){
	int bufferIndex = -1;
	int len;
	char* buffer;
	log_info("enter the receiveData from stack method");
	bufferIndex = memInstance->getBuffer(&buffer);
	if(bufferIndex == E_TRANC_BUFFER_FULL){
		logd_error("get buffer from stack, but buffer empty!!, sleep 2 second and return");
		sleep(2);
		return;
	}
	pthread_mutex_lock(&mutex_dataIn);
	len = read(fd_fifo_dataIn_r, buffer, 2);
	if(len != 2){
		 pthread_mutex_unlock(&mutex_dataIn);
		logd_error("read data from in fifo failed!!, %s", strerror(errno));
		return;
	}
	len = atoi(buffer);
	// len is in network bytes order
	len = TransContentAssis::SHORTN2L(len);
	if(len == 0){
		pthread_mutex_unlock(&mutex_dataIn);
		logd_error("get length from fifo in failed, data is: %s", buffer);
		return;
	}
	buffer[0]=0;
	buffer[1]=0;
	if(read(fd_fifo_dataIn_r, buffer, len)!= len){
		logd_error("read data from stack failed!!, %s", strerror(errno));
		pthread_mutex_unlock(&mutex_dataIn);
		return;
	}
	pthread_mutex_unlock(&mutex_dataIn);
	dumpPacket(buffer, len);
	TransContentPortMgr::getInstance()->dispatcher(buffer, bufferIndex);
	return;
}

void TransContentDispatcher::dumpPacket(const char* content, int len){
	char logPath[FILE_NAME_LEN] = {0};
	FILE* dumpFile ;
	int length = len;
	int offset = 0;
	int charIndex = 0;

	if(len <= 0){
		logd_error("dispathcer, dump packet the length is less than 0");
		return;
	}
	TransContentSetting::getDumpPacketFileName(logPath, FILE_NAME_LEN);

	dumpFile = fopen("logPath", "w+");
	if(dumpFile == NULL){
		logd_error("dispatcher, open dump file failed!, %s", strerror(errno));
		return;
	}

	for(int l1_i = 0; l1_i < length; ++l1_i){
		/// dump as bytes
		for(offset = 0, charIndex = l1_i; offset < 16 && charIndex < length; ++offset, ++charIndex){
			fprintf(dumpFile, "%02X", content[charIndex]);
		}

		/// when bytes less than 16, use space fill them
		while(offset < 16){
			fprintf(dumpFile, " ");
			++offset;
		}

		/// dump as char
		for(offset = 0, charIndex = l1_i; offset < 16 &&  charIndex < length; ++offset, ++charIndex){
			isprint(content[charIndex])?fprintf(dumpFile, "%c ", content[charIndex]):fprintf(dumpFile, "  ");
		}
		l1_i += 16;
	}
	fclose(dumpFile);
	logd_info("dispatcher, dump packet finished");
}
